package post_to_in_pkg.lexer;

import java.io.IOException;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Scanner;

import post_to_in_pkg.parser.OP_Keyword;

import java.util.ArrayList;
//may be deleted later 
import java.io.File;


public class Lexer {
	public static int line=1; 
    File readFile;
    Scanner scanner;
    Hashtable<String,Token> keywords;
    HashSet<String> illegal_words;
    StringBuffer buffer;
    boolean angle_bracket_recognition=true;
    boolean toggle_illegalword=true;
	public Lexer(File ref) throws IOException{
		if(ref==null) {
			//change input to Console or command line
			scanner=new Scanner(System.in);
		}
		else {
			readFile=ref;
			scanner=new Scanner(readFile);
			
		}
		buffer=new StringBuffer();
		keywords=new Hashtable<String,Token>(20);
		illegal_words=new HashSet<String>();
		//reserve some keywords
		//reserve(Token.OP_ADD);
		//reserve(Token.OP_DIV);
		reserve(ReservedToken.OP_ELSE);
		reserve(ReservedToken.OP_ENDIF);
		reserve(ReservedToken.OP_IF);
		//reserve(Token.OP_MUL);
		reserve(ReservedToken.OP_NOTIF);
		reserve(ReservedToken.OP_NOP);
		reserve(ReservedToken.OP_RETURN);
		//reserve(Token.OP_SUB);
		reserve(OP_Keyword.OP_ADD);
		reserve(OP_Keyword.OP_DIV);
		reserve(OP_Keyword.OP_MUL);
		reserve(OP_Keyword.OP_SUB);
		
		reserve(OP_Keyword.OP_CHECKLOCKTIMEVERIFY);
		reserve(OP_Keyword.OP_CHECKMULTISIG);
		reserve(OP_Keyword.OP_CHECKMULTISIGVERIFY);
		reserve(OP_Keyword.OP_CHECKSEQUENCEVERIFY);
		reserve(OP_Keyword.OP_CHECKSIG);
		reserve(OP_Keyword.OP_CHECKSIGVERIFY);
		reserve(OP_Keyword.OP_DROP);
		reserve(OP_Keyword.OP_DUP);
		reserve(OP_Keyword.OP_VERIFY);
		reserve(OP_Keyword.OP_EQUAL);
		reserve(OP_Keyword.OP_EQUALVERIFY);
		reserve(OP_Keyword.OP_HASH160);
		reserve(OP_Keyword.OP_HASH256);
		reserve(OP_Keyword.OP_RIPEMD160);
		reserve(OP_Keyword.OP_SHA1); reserve(OP_Keyword.OP_SHA256);
		
		//a more effective way to import keywords: 
	}

	public Token scan()throws IOException{
		buffer.setLength(0);
		if(scanner.hasNext()) { //still may wait for input here
			buffer.append(scanner.next());
			Token tok;
			
			if(keywords.containsKey(buffer.toString())) {
				tok=keywords.get(buffer.toString());
				tok.inline=line;
				if(tok.isOPKey()&&!((OP_Keyword)tok).isEnable()) {
					System.out.append("\n keyword:"+tok.toString()+"at near line "+line+"is disable and will be ignore");
					return scan();
				}
			}
			else {
				//add <content> recognition
				if(angle_bracket_recognition) {
					if(buffer.charAt(0)=='<') {
						String temp_next="";
						boolean has_next=scanner.hasNext();
						while(has_next&&!temp_next.endsWith(">")) {
							temp_next=scanner.next();
							buffer.append(" "+temp_next);
							has_next=scanner.hasNext();
						}
						if(!has_next&&!buffer.toString().endsWith(">")) {
							throw new IOException("Missing expected token: '>' "+"near token:"+buffer.toString()
								+"\nnear line:"+line);
						}
					}
				}
				tok=new Token(buffer.toString(),Tag.VALUE,line);
			}
			//update lexer-line info
			if(scanner.hasNextLine())
				line++;
			if(toggle_illegalword&&illegal_words.contains(tok.toString()))
				throw new IOException("Illegal word occurs at near line "+tok.inline);
			return tok;
		}
		else {
			return Token.Null;
		}
	}
	
	public void reserve(Token tok) {
		keywords.put(tok.toString(),tok);
	}
	
	public void toggle_illegalword_detect(boolean toggle){
		toggle_illegalword=toggle;
	}

	public void set_angle_bracket_recognition(boolean b) {
		angle_bracket_recognition=b;
	}
	
	public ArrayList<OP_Keyword> getKeywordsList(){
		ArrayList<OP_Keyword> list=new ArrayList<OP_Keyword>((int)(keywords.size()/0.7));
		//keywords.forEach(action); 
		
		for(Enumeration<Token> em=keywords.elements();em.hasMoreElements();) {
			Token tok=em.nextElement();
			if(tok instanceof OP_Keyword) {
				list.add((OP_Keyword)tok);
			}
		}
		return list;
	}
	
	public void setIllegalWord(String str) {
		illegal_words.add(str);
	}
	
	public void legalize(String tok) {
		if(illegal_words.contains(tok))
			illegal_words.remove(tok);
	}
}
